#pragma once

#include <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#include "luster_config.hh"
#include "luster_int.hh"
#include "luster_status.hh"

namespace luster {

#define LOGLEVEL_MAP(_) \
	_(DEBUG) \
	_(INFO) \
	_(ERROR)

enum LogLevel {
#define _(M_LEVEL) LOG_##M_LEVEL,
	LOGLEVEL_MAP(_)
#undef _
};

extern LogLevel MIN_LOGLEVEL;

struct Logger {
	Bool fatal{};
};

static inline Logger operator<<(Logger logger, LogLevel level) {
	return logger;
}

static inline Logger operator<<(Logger logger, Char const *text) {
	fputs(text, stderr);
	return logger;
}

static inline Logger operator<<(Logger logger, Bool b) {
	fputs(b ? "true" : "false", stderr);
	return logger;
}

static inline Logger operator<<(Logger logger, S32 x) {
	fprintf(stderr, "%" PRIi32, x);
	return logger;
}

static inline Logger operator<<(Logger logger, U32 x) {
	fprintf(stderr, "%" PRIu32, x);
	return logger;
}

static inline Logger operator<<(Logger logger, S64 x) {
	fprintf(stderr, "%" PRIi64, x);
	return logger;
}

static inline Logger operator<<(Logger logger, U64 x) {
	fprintf(stderr, "%" PRIu64, x);
	return logger;
}

struct LogEnd {};

extern Logger LOGGER;
extern Logger FATAL_LOGGER;
extern LogEnd LOGEND;

static inline Logger operator<<(Logger logger, LogEnd end__) {
	luster_UNUSED(end);
	fprintf(stderr, "\n");

	if (logger.fatal) {
		std::exit(1);
	}
	return logger;
}

void LogWrite(
	LogLevel level, char const *file, uintptr_t line, char const *fmt, ...);

void LogHead(LogLevel level, char const *file, uintptr_t line);

#if luster_CFG(DEBUGLOG)
#define LOG_DEBUG() luster::LOGGER << "DEBUG: "
#else
#define LOG_DEBUG() true ? luster::LOGGER : luster::LOGGER
#endif

#define LOG_INFO() ::luster::LOGGER << "INFO: "
#define LOG_ERROR() ::luster::LOGGER << "ERROR: "
#define LOG_FATAL() ::luster::FATAL_LOGGER << "FATAL: "
#define LOG_CONTINUE() ::luster::LOGGER

#define LOG(M_LEVEL) LOG_##M_LEVEL()

#define logprint(M_LEVEL, ...) \
	::luster::LogWrite(::luster::M_LEVEL, __FILE__, __LINE__, __VA_ARGS__)

#define logfatal(...) \
	do { \
		logprint(LOG_ERROR, "FATAL: " __VA_ARGS__); \
		exit(1); \
	} while (false)

#define logcheck(M_cond) \
	do { \
		if (M_cond) \
			break; \
		logfatal("CHECK: %s", #M_cond); \
	} while (0)

#define LOG_CHECK_EQ(M_lhs, M_rhs) \
	do { \
		auto __check_eq_lhs = (M_lhs); \
		auto __check_eq_rhs = (M_rhs); \
\
		if (__check_eq_lhs == __check_eq_rhs) \
			break; \
\
		LOG(ERROR) << __FILE__ << ":" << __LINE__ \
			   << " check equal failed: " \
			   << "(" #M_lhs " => " << __check_eq_lhs << ")" \
			   << " VS " \
			   << "(" #M_rhs " => " << __check_eq_rhs << ")" \
			   << LOGEND; \
		__test_context->error_count += 1; \
	} while (false)

} // namespace luster
